/*
 * Copyright 2011 Luis Atencio
 * 
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package net.rt.io.file;

import java.io.File;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.net.URLDecoder;
import java.nio.charset.Charset;
import java.util.LinkedHashMap;
import java.util.Map;

import javax.xml.parsers.DocumentBuilderFactory;

import net.rt.io.Utils;

import org.w3c.dom.Attr;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

/**
 * XML Read Strategy.
 * 
 * Provide a reading strategy for XML files.
 * 
 * @author Luis Atencio
 * 
 */
public class XMLFileStrategy extends AbstractFileStrategy {

	public XMLFileStrategy(String path, String filename) {
		super(path, filename);
	}

	@Override
	public Map<String, String> bindProperties() {

		URL url = Utils.locate(path, fileName);

		if (url == null) {
			throw new IllegalStateException("Cannot locate file with path: "
					+ path + " and file name " + fileName);
		}

		return load(url);
	}

	private Map<String, String> load(URL url) throws RuntimeException {

		final Charset UTF8 = Charset.forName("UTF-8");
		File file;
		try {
			file = new File(URLDecoder.decode(url.getPath(), UTF8.name()));
		} catch (UnsupportedEncodingException e1) {
			throw new RuntimeException(e1);
		}

		if (file == null || file.isDirectory()) {
			throw new RuntimeException("Cannot load configuration file");
		}

		try {			
			XPathReader xpathReader = new XPathReader(url.getPath());
			return xpathReader.getxPathExpMap();
		} catch (Exception e) {
			throw new RuntimeException(
					"Unable to load the configuration from the URL " + url, e);
		} finally {
			
		}
	}

	public static class XPathReader {

		private String xmlFile;
		private Document doc;
		private Map<String, String> xPathExp = new LinkedHashMap<String, String>();

		public XPathReader(String xmlFile) {
			this.xmlFile = xmlFile;
			mapXpathObjects();
		}

		private void mapXpathObjects() {
			try {
				DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
				factory.setNamespaceAware(true);
				doc = factory.newDocumentBuilder().parse(xmlFile);
				Node root = doc.getDocumentElement();
				root.normalize();
				crawlDoc(root);
			} catch (Exception ex) {
				ex.printStackTrace();
			}
		}
		
		private void crawlDoc(Node root) {
			NodeList children = root.getChildNodes();
		    for(int i = 0; i < children.getLength(); i++) {
		    	Node child = children.item(i);
		    	
		    	// Map every single text node in the XML file
		    	if(child.getNodeType() == Node.TEXT_NODE) {
		    		if(child.getNodeValue() != null && child.getNodeValue().trim().length() != 0) {
		    			xPathExp.put(generateXPath(child.getParentNode()), child.getNodeValue());
		    		}
		    	}
		    	else {
		    		crawlDoc(child);
		    	}
		    }
		}
		
		private String generateXPath(Node node) {
			
			if(node.getParentNode() == null || 
					node.getNodeName().equals(node.getOwnerDocument().getDocumentElement().getNodeName())) {
				return "/" + node.getNodeName();
			}
			
			switch(node.getNodeType()) {
				case Node.ATTRIBUTE_NODE:
					node = ((Attr)node).getOwnerElement();
					return generateXPath(node.getParentNode()) + "/@" + node.getNodeName();
				case Node.ELEMENT_NODE:
			    default:
			    	return generateXPath(node.getParentNode()) + "/" + node.getNodeName();
			}
		}
		
		public Map<String, String> getxPathExpMap() {
			return xPathExp;
		}
	}
}
